﻿Imports System.Net.Sockets
Imports System.ComponentModel
Imports System.Runtime.InteropServices
Imports System.Text

Public Class WebPort

#Region "TCP通信"

    Public TcpComm As TcpClient
    Private _TcpConnected As Boolean = False
    Public print_bytes As Action(Of Byte()) = Sub(b)
                                                  For i As Integer = 0 To b.Length - 1
                                                      Console.Write(b(i).ToString("X2") + " ")
                                                  Next
                                                  Console.Write(vbCrLf)
                                              End Sub

    Public Event OnMessage As Action(Of Byte())

    Private _IPAddress As Net.IPAddress
    <DescriptionAttribute("系统的IP地址")>
    Public Property IPAddress As String
        Get
            Return _IPAddress.ToString
        End Get
        Set(value As String)
            Try
                _IPAddress = Net.IPAddress.Parse(value)
            Catch ex As Exception
                MsgBox(ex.ToString)
            End Try
        End Set
    End Property

    <DescriptionAttribute("本地TCP端口号")>
    Public Property LocalTcpPort As Integer

    <DescriptionAttribute("TCP/IP连接状态")>
    Public ReadOnly Property TcpConnected As Boolean
        Get
            Return _TcpConnected
        End Get
    End Property

    Public ReadOnly Property Ready As Boolean
        Get
            Return _TcpConnected
        End Get
    End Property

    <Description("Tcp接收缓存数据大小")>
    Public ReadOnly Property BytesToRead As Integer
        Get
            If TcpComm Is Nothing Then
                Return 0
            End If
            If TcpComm.Client Is Nothing Then
                Return 0
            End If
            Return TcpComm.Client.Available
        End Get
    End Property

    <DescriptionAttribute("TCP发送缓存")>
    Public Property TcpSendBuffer As Byte() = New Byte(0) {0}

    Private _TcpSendBufferSize As Integer
    Public Property TcpSendBufferSize As Integer
        Get
            Return _TcpSendBufferSize
        End Get
        Set(value As Integer)
            ReDim TcpSendBuffer(value)
            _TcpSendBufferSize = value
        End Set
    End Property

    ''' <summary>
    ''' 与SIMOTION系统建立连接
    ''' </summary>
    ''' <param name="valTimeOut">连接的超时时间，默认1.5秒</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function TcpConnect(ip As String, port As Integer, Optional valTimeOut As Integer = 1500) As Boolean
        If TcpComm Is Nothing Then
            '关于TCP_NODELAY的说明：https://zhuanlan.zhihu.com/p/90057929
            TcpComm = New TcpClient With {
                .NoDelay = True
            }
        ElseIf TcpComm.Client Is Nothing Then
            TcpComm = New TcpClient()
        End If
        Try
            IPAddress = ip
            LocalTcpPort = port
            If (Not TcpComm.ConnectAsync(_IPAddress, LocalTcpPort).Wait(valTimeOut)) Then
                MsgBox("Connecting Time Out.")
                TcpSetOffline()
                Return False
            Else
                TcpSetOnline()
                Return True
            End If
        Catch ex As Exception
            MsgBox(ex.Message)
            TcpSetOffline()
            Return False
        End Try
    End Function

    ''' <summary>
    ''' 断开TCP连接
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub Close()
        TcpSetOffline()
    End Sub

    '设置为上线状态，配置TcpClient
    Private Sub TcpSetOnline()
        _TcpConnected = True
    End Sub
    '设置为离线状态，释放资源
    Private Sub TcpSetOffline()
        _TcpConnected = False
        If TcpComm IsNot Nothing Then
            TcpComm.Close()
        End If
    End Sub

    ''' <summary>
    ''' 查询当前的连接状态
    ''' </summary>
    ''' <returns>在线返回true，掉线返回false</returns>
    ''' <remarks></remarks>
    Public Function GetTcpConnectState() As Boolean
        If TcpComm.Client Is Nothing Then
            TcpSetOffline()
            Return False
        Else
            If TcpComm.Client.Poll(1000, SelectMode.SelectRead) Then
                TcpSetOffline()
                Return False
            Else
                TcpSetOnline()
                Return True
            End If
        End If
    End Function

    ''' <summary>
    ''' 发送TcpSendBuffer中的数据
    ''' </summary>
    ''' <remarks></remarks>
    Public Overloads Sub TcpSend()
        Write(TcpSendBuffer, 0, TcpSendBuffer.Length)
    End Sub

    ''' <summary>
    ''' 发送帧数据
    ''' </summary>
    ''' <param name="valSendBuffer">待发送的数据</param>
    ''' <remarks></remarks>
    Public Overloads Sub Write(valSendBuffer As Byte(), index As Integer, len As Integer)
        If _TcpConnected Then
            Try
                TcpComm.Client.Send(valSendBuffer)
            Catch ex As Exception
                MsgBox(ex.ToString)
                TcpSetOffline()
            End Try
        End If
    End Sub

    ''' <summary>
    ''' 发送文本信息
    ''' </summary>
    ''' <param name="msg">信息内容</param>
    Public Overloads Sub Write(msg As String)
        If _TcpConnected Then
            Try
                Dim buf(msg.Length - 1) As Byte
                buf = Encoding.ASCII.GetBytes(msg)
                Console.WriteLine(msg)
                TcpComm.Client.Send(buf)
            Catch ex As Exception
                Console.WriteLine(ex.Message)
            End Try
        End If
    End Sub

    Public Function ReadByte() As Byte
        Dim b(0) As Byte
        TcpComm.Client.Receive(b, 1, SocketFlags.None)
        Return b(0)
    End Function

    Public Function Read(buffer() As Byte, offset As Integer, count As Integer) As Integer
        Return TcpComm.Client.Receive(buffer, offset, count, SocketFlags.None)
    End Function

    Public Sub DiscardInBuffer()
        Try
            If TcpComm.Client.Available > 0 Then
                Dim buf() As Byte
                ReDim buf(TcpComm.Client.Available)
                TcpComm.Client.Receive(buf)
                Debug.Print("[Discard Buffer]")
                print_bytes(buf)
            End If
        Catch ex As Exception
            Debug.Print("[Discard Error]" + ex.Message)
        End Try
    End Sub

#End Region

End Class

